/***WebViewJavascriptBridge By GYLite 2022-12-26
 * 网页与app的桥接功能类，提供桥接通讯功能
*/
class WebViewJavascriptBridge {
	public static base64encodechars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";    
	public static CUSTOM_PROTOCOL_SCHEME = 'yy';
    public static QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';
	private static _messageHandler:Function;
	public messagingIframe:HTMLIFrameElement;
    public sendMessageQueue:any[];
    public receiveMessageQueue:any[];
    public messageHandlers:any;
	public responseCallbacks:any[];
    private uniqueId:number;
	constructor() {		
		let s = this;
		WebViewJavascriptBridge._instance = s;
		s.messageHandlers = {};
		s.sendMessageQueue = [];
		s.receiveMessageQueue = [];
		s.responseCallbacks = [];		
		s.uniqueId = 1;
		var doc = document;    
		s._createQueueReadyIframe(doc);
	}
	public base64encode(str:string):string
	{        
		if (str === undefined) 
		{            
			return str;        
		}        
		var out:string, i:number, len:number;
		var c1:number, c2:number, c3:number;
		var base64encodechars:string;
		base64encodechars = WebViewJavascriptBridge.base64encodechars; 
		len = str.length;
		i = 0;
		out = "";        
		while (i < len) {
			c1 = str.charCodeAt(i++) & 0xff;            
			if (i == len) {                
				out += base64encodechars.charAt(c1 >> 2);
				out += base64encodechars.charAt((c1 & 0x3) << 4);
				out += "==";                
				break;            
			}
			c2 = str.charCodeAt(i++);
			if (i == len) {                
				out += base64encodechars.charAt(c1 >> 2);
				out += base64encodechars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4));
				out += base64encodechars.charAt((c2 & 0xf) << 2);                
				out += "=";
				break;            
			}            
			c3 = str.charCodeAt(i++);
			out += base64encodechars.charAt(c1 >> 2);
			out += base64encodechars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4)); 
			out += base64encodechars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6));            
			out += base64encodechars.charAt(c3 & 0x3f);        
		}        
		return out;    
	}    
	private _createQueueReadyIframe(doc:Document) 
	{
		let s = this;
		s.messagingIframe = doc.createElement('iframe');        
		s.messagingIframe.style.display = 'none';        
		doc.documentElement.appendChild(s.messagingIframe);
	}    
	private isAndroid() 
	{        
		var ua = navigator.userAgent.toLowerCase();
        var isA = ua.indexOf("android") > -1;        
		if (isA) {            
			return true;        
		}        
		return false;    
	}    
	private isIphone() 
	{        
		var ua = navigator.userAgent.toLowerCase();        
		var isIph = ua.indexOf("iphone") > -1;        
		if (isIph) 
		{            
			return true;        
		}        
		return false;    
	}    
	public init(messageHandler:Function) 
	{        
		let s =this;
		if (WebViewJavascriptBridge._messageHandler) 
		{            
			throw new Error('WebViewJavascriptBridge.init called twice');        
		}        
		WebViewJavascriptBridge._messageHandler = messageHandler;        
		var receivedMessages = s.receiveMessageQueue;
        s.receiveMessageQueue = null;        
		for (var i = 0; i < receivedMessages.length; i++) {            
			s._dispatchMessageFromNative(receivedMessages[i]);        
		}    
	}    
	private send(data:any, responseCallback:Function) {        
		let s =this;
		s.doSend({data: data}, responseCallback);    
	}    
	private registerHandler(handlerName:string, handler) 
	{
		let s = this;   
		s.messageHandlers[handlerName] = handler;    
	}    
	private callHandler(handlerName:string, data:any, responseCallback:Function) 
	{      
		let s = this;  
		s.doSend({handlerName: handlerName,data: data}, responseCallback);    
	} 
	public doSend(message:any, responseCallback:Function=null) 
	{        
		let s = this;
		if (responseCallback) 
		{            
			var callbackId = 'cb_' + (s.uniqueId++) + '_' + new Date().getTime();            
			s.responseCallbacks[callbackId] = responseCallback;            
			message.callbackId = callbackId;        
		}        
		s.sendMessageQueue.push(message);        
		s.messagingIframe.src = WebViewJavascriptBridge.CUSTOM_PROTOCOL_SCHEME + '://' + WebViewJavascriptBridge.QUEUE_HAS_MESSAGE;    
	}    
	private _fetchQueue() 
	{        
		let s = this;
		var messageQueueString = JSON.stringify(s.sendMessageQueue);        
		s.sendMessageQueue = [];        
		if (s.isIphone()) 
		{            
			return messageQueueString;        
		} else if (s.isAndroid()) 
		{            
			s.messagingIframe.src = WebViewJavascriptBridge.CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);        
		}    
	}    
	private _dispatchMessageFromNative(messageJSON) 
	{        
		setTimeout(function(s:WebViewJavascriptBridge) {
			var message = JSON.parse(messageJSON);            
			var responseCallback;            
			if (message.responseId) 
			{                
				responseCallback = s.responseCallbacks[message.responseId];                
				if (!responseCallback) 
				{
					return;                
				}                
				responseCallback(message.responseData);                
				delete s.responseCallbacks[message.responseId];            
			} else {                
				if (message.callbackId) 
				{                    
					var callbackResponseId = message.callbackId;                    
					responseCallback = function(responseData) 
					{                        
						s.doSend({responseId: callbackResponseId,responseData: responseData});                    
					};
				}                
				var handler = WebViewJavascriptBridge._messageHandler;                
				if (message.handlerName) 
				{                    
					handler = s.messageHandlers[message.handlerName];                
				}                
				try {                    
					handler(message.data, responseCallback);                
				} 
				catch (exception) 
				{                    
					if (typeof console != 'undefined') 
					{                        
						console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);                    
					}                
				}            
			}        
		}, 0, this);    
	}    
	private _handleMessageFromNative(messageJSON) 
	{        
		// console.log(messageJSON);   
		let s =this;     
		if (s.receiveMessageQueue) 
		{            
			s.receiveMessageQueue.push(messageJSON);        
		} else {            
			s._dispatchMessageFromNative(messageJSON);        
		}    
	}    

	public static init:Function;
	public static send:Function;
	public static registerHandler:Function;
	public static callHandler:Function;
	public static _fetchQueue:Function;
	public static _handleMessageFromNative:Function;
	private static _instance:WebViewJavascriptBridge;
	public static getInstance():WebViewJavascriptBridge
	{
		if(WebViewJavascriptBridge._instance == null)
			new WebViewJavascriptBridge;
		return WebViewJavascriptBridge._instance;
	}
	public static createBridge():void
	{
		if(WebViewJavascriptBridge && WebViewJavascriptBridge.init!=null)return;
		let bridge = WebViewJavascriptBridge.getInstance();
		WebViewJavascriptBridge.init = bridge.init.bind(bridge);
		WebViewJavascriptBridge.send = bridge.send.bind(bridge);
		WebViewJavascriptBridge.registerHandler = bridge.registerHandler.bind(bridge);
		WebViewJavascriptBridge.callHandler = bridge.callHandler.bind(bridge);
		WebViewJavascriptBridge._fetchQueue = bridge._fetchQueue.bind(bridge);
		WebViewJavascriptBridge._handleMessageFromNative = bridge._handleMessageFromNative.bind(bridge);
		var doc = document;    		
		var readyEvent:any = doc.createEvent('Events');    
		readyEvent.initEvent('WebViewJavascriptBridgeReady');    
		readyEvent.bridge = WebViewJavascriptBridge;    
		doc.dispatchEvent(readyEvent);
	}	
}